<?php

/*
 * Copyright 2008 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#namespace tehframework\mvc;

#use \ReflectionException;

class ViewFactoryImpl implements ViewFactory
{
	const VIEW_CLASS_SUFFIX = 'View';
	
	/**
	 * @var array<string>
	 */
	private static $namespaceSeparators = array('.', '/');
	
	/**
	 * @var string
	 */
	private $namespace;
	
	/**
	 * @var bool
	 */
	private $appendSuffix;
	
	/**
	 * @param  string? $namespace
	 *         An optional namespace of the views used when creating
	 *         a view class.
	 * @param  bool? $appendSuffix
	 *         Whether to append the {@link VIEW_CLASS_SUFFIX} when
	 *         creating a view class.
	 *         Defaults to FALSE.
	 * @see    ViewFactoryImpl::setNamespace()
	 * @see    ViewFactoryImpl::appendSuffix()
	 */
	public function __construct($namespace = null, $appendSuffix = null)
	{
		$this->setNamespace($namespace)->setAppendSuffix($appendSuffix);
	}
	
	/**
	 * Sets a namespace of the view classes.
	 *
	 * @param  string? $namespace
	 *         A namespace of the view classes or NULL if no namespace
	 *         should be used.
	 * @return ViewFactoryImpl
	 */
	public function setNamespace($namespace)
	{
		$this->namespace = empty($namespace)
			? '' : $namespace . T_NS_SEPARATOR;
		
		return $this;
	}
	
	/**
	 * Sets a flag determining whether to append {@link VIEW_CLASS_SUFFIX}
	 * to the view class.
	 *
	 * @param  bool $appendSuffix
	 *         Whether to append suffix to the view class.
	 * @return ViewFactoryImpl
	 */
	public function setAppendSuffix($appendSuffix)
	{
		$this->appendSuffix = (bool)$appendSuffix;
		
		return $this;
	}
	
	/**
	 * Creates a View instance from {@link $viewName view name}.
	 * 
	 * All dots and slashes in the {@link $viewName} are replaced
	 * with double colon.
	 * 
	 * If specified, view class is prefixed with
	 * {@link setNamespace() namespace} and suffixed with
	 * {@link VIEW_CLASS_SUFFIX}.
	 * 
	 * For example, if the $viewName is 'Post.Read',
	 * namespace is set to 'myapp::views' and suffixing is enabled,
	 * the resulting class name will be 'myapp::views::Post::ReadView'.
	 * 
	 * @param  string $viewName
	 *         A name of the view to create.
	 * @return View?
	 *         An instance of the view or NULL if no view was found.
	 * @throws ReflectionException
	 *         If the created object does not have a parameterless constructor.
	 * @see    ViewFactoryImpl::setNamespace()
	 * @see    ViewFactoryImpl::appendSuffix()
	 */
	public function createView($viewName)
	{
		try
		{
			$viewClass = new ReflectionClass(
				$this->getViewClassFrom($viewName)
			);
			
			if (!$viewClass->isInstantiable())
			{
				return null;
			}
		}
		catch (ReflectionException $x)
		{
			return null;
		}
		
		return $viewClass->newInstance();
	}
	
	/**
	 * @param  string $viewName
	 * @return string
	 */
	private function getViewClassFrom($viewName)
	{
		$viewClass = $this->namespace . str_replace(
			self::$namespaceSeparators, T_NS_SEPARATOR, $viewName
		);
		
		if ($this->appendSuffix)
		{
			$viewClass .= self::VIEW_CLASS_SUFFIX;
		}
		
		return $viewClass;
	}
}